package com.example.stringbufferlogger.service.impl;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.stringbufferlogger.Utils.CompareUtil;
import com.example.stringbufferlogger.Utils.JoltUtil;
import com.example.stringbufferlogger.config.TaskStatus;
import com.example.stringbufferlogger.constants.ContextConstants;
import com.example.stringbufferlogger.dto.*;
import com.example.stringbufferlogger.entity.*;
import com.example.stringbufferlogger.mapper.*;
import com.example.stringbufferlogger.service.CompareDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CompareDateServiceImpl implements CompareDateService {
    private static final String LOG_REGEX = ".*?(2010120000).*?apiId=(.*?),.*?sendTime=(.*?),.*?send\\[\\d{16}(.*?)\\].*?return\\[(.*?)\\]";
    private static final String SUCCESS = "1";
    private static final String FAIL = "0";
    private static final String ENTER_PATH = "ENTER_PATH";
    private static final String CURRENT_PAGE = "CURRENT_PAGE";

    private static final List<String> PARAM_LIST = Arrays.asList("policyno","gpolicyno");
    private static final String INSERT_SIZE = "INSERT_SIZE";
    private static final String PAGE_SIZE = "PAGE_SIZE";
    private static final String SUBMIT_SIZE = "SUBMIT_SIZE";
    private final ExecutorService executorService = new ThreadPoolExecutor(
            Integer.max(2, Runtime.getRuntime().availableProcessors()  + 1),
            Integer.max(8, Runtime.getRuntime().availableProcessors() * 2),
            1,
            TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(256),
            new CustomizableThreadFactory("log-compare-"),
            new ThreadPoolExecutor.CallerRunsPolicy());

    @Resource
    private LogRecordMapper logRecordMapper;
    @Resource
    private QueryCompareMapper queryCompareMapper;
    @Resource
    private ZYXLifeCommonMapper zyxLifeCommonMapper;
    @Resource
    private ZYXDbCommonMapper zyxDbCommonMapper;
    @Resource
    private ConfigEnumerationTableMapper configEnumerationTableMapper;
    @Resource
    private GPSDealCompareDataLogMapper dealCompareDataLogMapper;
    @Resource
    private GPSDealCompareDataMapper dealCompareDataMapper;
    @Resource
    private  ReplayResultMapper replayResultMapper;
    @Resource
    private InterfaceMappingConfigMapper interfaceMappingConfigMapper;

    @Override
    public ResponseEntity<String> dataCompare(CompareDTO compareDTO) {
        //查询配置CP_CONFIG_PARALLELL表
        List<ConfigParallellTableEntityDTO> configParallellTable = zyxLifeCommonMapper.selectConfigParallellTable();
        //查询交易号配置表CONFIG_APIID_POLICY
        List<ConfigApiIDPolicyDTO> apiConfigList = zyxLifeCommonMapper.selectConfigApiIdPolicyById();
        List<LogRecord> records = compareDTO.getRecords();
        String apiType = compareDTO.getApiType();
        for (LogRecord record: records) {
            if (TaskStatus.isStop()) {
                break;
            }
            //交易号为交易类型时执行
            String policynolog = null;
            try {
                //调用新旧系统
                //QueryCompare compareData = callOldAndNewSystemCompare(record);
                //查询已经完成比对的数据
                QueryCompare result =  replayResultMapper.selectResByRequestId(record.getRequestId());
                //判断是否为交易类型 0-查询 1-交易 查询类型不进行数据比对
                InterfaceMappingConfig interfaceMappingConfig = interfaceMappingConfigMapper.selectByInterfaceUrl(record.getPath());
//                if (apiType.equals("0")) {
                if (interfaceMappingConfig.getWriteFlag().equals("0")) {
                    continue;
                }
                String apiId = record.getRequestId();
                //保单号类型
                if (record.getPolicyNo()!=null && !"".equals(record.getPolicyNo())) {
                    log.info("{}交易号，保单号类型，查询比对开始", record.getRequestId());
                    //从报文中获取参数
                    String[] policynos = record.getPolicyNo().split(",");
                    for (String policyno: policynos) {
                        if (StringUtils.isEmpty(policyno.trim())) {
                            continue;
                        }
                        policynolog = policyno;
                        //获取branchid
                        String branchid = zyxLifeCommonMapper.selectBranchIdByPolicyNo(policyno);
                        if (StringUtils.isEmpty(branchid)) {
                            branchid = zyxLifeCommonMapper.selectBranchIdByGPolicyNo(policyno);
                        }
                        //查询数据库比较数据
                        getDealCompareData(apiId, configParallellTable, policyno, result, record.getType(),branchid);
                    }
                }
                //团单号类型
                if (record.getGpolicyNo()!=null && !"".equals(record.getGpolicyNo())) {
                    log.info("{}交易号，团单号类型，查询比对开始", record.getRequestId());
                    String gpolicyno = record.getGpolicyNo();
                    if (StringUtils.isEmpty(gpolicyno.trim())) {
                        continue;
                    }
                    //获取branchid
                    String branchid = zyxLifeCommonMapper.selectBranchIdByGPolicyNo(gpolicyno);
                    if (StringUtils.isEmpty(branchid)) {
                        branchid = zyxLifeCommonMapper.selectBranchIdByPolicyNo(gpolicyno);
                    }
                    List<String> policynos = zyxLifeCommonMapper.getPolicyNosByGPolicyNo(gpolicyno, branchid);
                    for (String policyno: policynos) {
                        if (StringUtils.isEmpty(policyno)) {
                            break;
                        }
                        policynolog = policyno;
                        //查询数据库比较数据
                        getDealCompareData(apiId, configParallellTable, policyno, result, record.getType(),branchid);
                    }
                }
                //特殊类型
                if (record.getApiType().equals("3")) {
                    log.info("{}交易号，特殊类型，查询比对开始", record.getRequestId());
                    List<ConfigApiIDPolicyDTO> collect = apiConfigList.stream().filter(p -> p.getApiId().equals(apiId)).collect(Collectors.toList());
                    if (collect.size() == 0) {
                        log.info("{}交易号未找到配置信息", apiId);
                    }
                    for (ConfigApiIDPolicyDTO config: collect) {
                        dealType3(record, result, apiId, config);
                    }
                }
            } catch (Exception e) {
                log.error("{}交易号，{}保单比对失败",record.getRequestId(),policynolog, e);
            }
        }
        return ResponseEntity.ok("对比成功！");
    }

    /**
     * 调用新旧系统
     * @param record
     * @return
     */
    private QueryCompare callOldAndNewSystemCompare(LogRecord record) {
        QueryCompareRes queryCompareRes = new QueryCompareRes();
        queryCompareRes.setId(record.getId());
        queryCompareRes.setRequestBody(record.getRequestBody());
        queryCompareRes.setRequestId(record.getRequestId());
        //LogRecordMapper recordMapper = (LogRecordMapper) ApplicationContextUtil.getBean("logRecordMapper");
        SubEntityDTO substr = logRecordMapper.getSubstr(record.getRequestId());
        queryCompareRes.setSubRule(substr.getSubRule());
        queryCompareRes.setSubType(substr.getSubType());
        QueryCompare compareData = dealData(queryCompareRes);
        queryCompareMapper.insert(compareData);
        return compareData;
    }

    /**
     * 报文数据比对公共方法
     * @param
     */
    public QueryCompare dealData(QueryCompareRes queryCompareRes){
        log.info("开始处理查询交易号:{}",queryCompareRes.getRequestId());
        QueryCompare queryCompare = new QueryCompare();
        queryCompare.setLogId(queryCompareRes.getId());
        queryCompare.setApiId(queryCompareRes.getRequestId());
        queryCompare.setReqMsg(queryCompareRes.getRequestBody());
        queryCompare.setCreateTime(new Date());
        String reqMsg = queryCompareRes.getRequestBody();
        try {
            String newResponse = JoltUtil.call(JoltUtil.NEW_SYS, reqMsg, queryCompareRes.getId());
            queryCompare.setNewRes(newResponse);
            String oldResponse = JoltUtil.call(JoltUtil.OLD_SYS,reqMsg, queryCompareRes.getId());
            queryCompare.setOldRes(oldResponse);
            queryCompare.setResult(this.FAIL);
            if (!StringUtils.isBlank(newResponse) && !StringUtils.isBlank(oldResponse)){
                String newRes = null;
                String oldRes = null;
                if ("0".equals(queryCompareRes.getSubType())){
                    newRes = CompareUtil.dealSubString(newResponse, queryCompareRes.getSubRule());
                    oldRes = CompareUtil.dealSubString(oldResponse, queryCompareRes.getSubRule());
                }else{
                    newRes = CompareUtil.dealRemoveString(newResponse, queryCompareRes.getSubRule());
                    oldRes = CompareUtil.dealRemoveString(oldResponse, queryCompareRes.getSubRule());
                }
                log.info("新系统报文处理后{}",newRes);
                log.info("旧系报文处理后{}",oldRes);
                if (!StringUtils.isBlank(newRes) && newRes.equals(oldRes)){
                    queryCompare.setResult(this.SUCCESS);
                }
            }
            queryCompare.setUpdateTime(new Date());
        } catch (Exception e) {
            queryCompare.setResult(this.FAIL);
            queryCompare.setUpdateTime(new Date());
            log.error("CP_LOG_RECORD.id:[{}]调用综意险异常", queryCompare.getLogId(), e);
        }finally {
            return queryCompare;
        }
    }

    /**
     * 查询新旧数据库数据进行比较
     * @param apiId
     * @param configParallellTable
     * @param policyno
     * @param compareData
     * @param source
     * @return
     */
    private  List<GPSDealCompareData> getDealCompareData(String apiId, List<ConfigParallellTableEntityDTO> configParallellTable, String policyno, QueryCompare compareData, String source, String branchid) {
        //根据配置表拼接sql
        for (ConfigParallellTableEntityDTO table: configParallellTable) {
            if (StringUtils.isEmpty(table.getSelectsql())) {
                continue;
            }
            String sql = String.format(table.getSelectsql(), policyno,branchid,branchid);
            String tableName = table.getTableName();
            //查询比较数据
            queryAndCompareDataBySql(apiId, policyno, compareData, source, sql, tableName);
        }
        return null;
    }

    /**
     * 查询比较数据
     * @param apiId
     * @param policyno
     * @param compareData
     * @param source
     * @param sql
     * @param tableName
     */
    private void queryAndCompareDataBySql(String apiId, String policyno, QueryCompare compareData, String source, String sql, String tableName) {
        List<String> zyxlifResults = null;
        List<String> zyxdbResults = null;
        Date startTime = new Date();
        Date endTime = null;
        try {
            //查询新库
            zyxlifResults = zyxLifeCommonMapper.selectDataByPolicyNo(sql);
            //查询旧库
            zyxdbResults = zyxDbCommonMapper.selectDataByPolicyNo(sql);
            endTime = new Date();
        } catch (Exception e) {
            log.error("查询数据库失败, sql为{}", sql,e);
            endTime = new Date();
            //日志表记录
            insertLog(compareData.getApiId(), compareData.getLogId(), compareData.getId(), policyno,"0","0",
                    ContextConstants.RESULT_QUERY_FAIL, tableName, startTime,endTime);
            return;
        }
        if (zyxdbResults.size() != zyxlifResults.size()) {
            log.info("{}交易号新旧数据库结果数量不同,sql:{}", apiId, sql);
            //查询结果数量不同 日志表记录
            insertLog(compareData.getApiId(), compareData.getLogId(), compareData.getId(), policyno,String.valueOf(zyxlifResults.size()),String.valueOf(zyxdbResults.size()),
                    ContextConstants.RESULT_NUM_NO_EQ, tableName, startTime,endTime);
            //差异数据入库
            saveOldAndNewDBData(apiId, tableName, zyxlifResults, zyxdbResults, policyno, compareData, source);
            return;
        }
        if (zyxdbResults.size() == 0) {
            //查询数据为空 日志表记录
            insertLog(compareData.getApiId(), compareData.getLogId(), compareData.getId(), policyno,"0","0",
                    ContextConstants.RESULT_NO_RESULT, tableName, startTime,endTime);
            return;
        }
        //比较两个集合
        compareAndSaveResult(apiId, sql, zyxlifResults, zyxdbResults, policyno, compareData, tableName, source, startTime, endTime);
    }

    public void insertLog(String apiId, Long logId,Long compareId, String policyno, String newNum,
                          String oldNum, String flag, String table, Date startTime, Date endTime){
        GPSDealCompareDataLog compareLog = new GPSDealCompareDataLog();
        compareLog.setApiId(apiId);
        compareLog.setLogID(logId);
        compareLog.setCompareId(compareId);
        compareLog.setPolicyno(policyno);
        compareLog.setNewDataNum(newNum);
        compareLog.setOldDataNum(oldNum);
        compareLog.setFlag(flag);
        compareLog.setTableName(table);
        compareLog.setStartTime(CompareUtil.getFormatDateStr(startTime,"yyyy-MM-dd HH:mm:ss"));
        compareLog.setEndTime(CompareUtil.getFormatDateStr(endTime,"yyyy-MM-dd HH:mm:ss"));
        dealCompareDataLogMapper.insert(compareLog);
    }


    private void saveOldAndNewDBData(String apiId, String tableName, List<String> zyxlifResults, List<String> zyxdbResults, String policyno, QueryCompare deal, String source) {
        ConfigEnumerationTable insertConfig = configEnumerationTableMapper.queryValueByType(INSERT_SIZE);
        Integer insertSize  = Integer.valueOf(insertConfig.getValue());
        List<GPSDealCompareData> dataList = new ArrayList<>();
        for (int i = 0; i < zyxlifResults.size(); i++){
            GPSDealCompareData data = new GPSDealCompareData();
            data.setCompareId(deal.getId());
            data.setLogID(deal.getLogId());
            data.setApiId(apiId);
            data.setNewData(zyxlifResults.get(i));
            data.setTableName(tableName);
            data.setPolicyno(policyno);
            data.setSource(source);
            dataList.add(data);
            if (dataList.size() == insertSize) {
                dealCompareDataMapper.insertBatchSomeColumn(dataList);
                dataList = new ArrayList<>();
            }
        }
        if (dataList.size()>0) {
            dealCompareDataMapper.insertBatchSomeColumn(dataList);
            dataList = new ArrayList<>();
        }
        for (int i = 0; i < zyxdbResults.size(); i++){
            GPSDealCompareData data = new GPSDealCompareData();
            data.setCompareId(deal.getId());
            data.setLogID(deal.getLogId());
            data.setApiId(apiId);
            data.setOldData(zyxdbResults.get(i));
            data.setTableName(tableName);
            data.setPolicyno(policyno);
            data.setSource(source);
            dataList.add(data);
            if (dataList.size() == insertSize) {
                dealCompareDataMapper.insertBatchSomeColumn(dataList);
                dataList = new ArrayList<>();
            }
        }
        if (dataList.size()>0) {
            dealCompareDataMapper.insertBatchSomeColumn(dataList);
        }
    }

    /**
     * 比较与保存结果
     * @param apiId
     * @param sql
     * @param zyxlifResults
     * @param zyxdbResults
     * @param policyno
     * @param deal
     * @param tableName
     */
    private void compareAndSaveResult(String apiId, String sql, List<String> zyxlifResults, List<String> zyxdbResults, String policyno,
                                      QueryCompare deal, String tableName, String source, Date startTime, Date endTime) {
        List<String> zyxdbSort = zyxdbResults.stream().sorted().collect(Collectors.toList());
        List<String> zyxlifeSort = zyxlifResults.stream().sorted().collect(Collectors.toList());
        for (int i = 0; i < zyxdbSort.size(); i++) {
            if (!zyxdbSort.get(i).equals(zyxlifeSort.get(i))) {
                log.info("{}交易号新旧数据库对比结果不同,sql:{}", apiId, sql);
                //结果不同，日志表记录
                saveOldAndNewDBData(apiId, tableName, zyxlifResults, zyxdbResults, policyno, deal, source);
                //数据落库
                insertLog(deal.getApiId(),deal.getLogId(),deal.getId(), policyno,String.valueOf(zyxdbResults.size()),String.valueOf(zyxdbResults.size()),
                        ContextConstants.RESULT_EQ_FAIL,tableName, startTime, endTime);
                break;
            }
        }
        log.info("{}交易号新旧数据库对比结果相同,sql:{}", apiId, sql);
        //结果相同 日志表记录
        insertLog(deal.getApiId(),deal.getLogId(),deal.getId(), policyno,String.valueOf(zyxdbResults.size()),String.valueOf(zyxdbResults.size()),
                ContextConstants.RESULT_EQ_SUC,tableName, startTime, endTime);
    }

    private void dealType3(LogRecord record, QueryCompare compareData, String apiId, ConfigApiIDPolicyDTO config) {
        //获取参数
        List<String> params = getParams(record, config);
        for (String param: params) {
            try {
                //获取branchid
                String branchid = zyxLifeCommonMapper.selectBranchIdByPolicyNo(param);
                if (StringUtils.isEmpty(branchid)) {
                    branchid = zyxLifeCommonMapper.selectBranchIdByGPolicyNo(param);
                }
                branchid = StringUtils.isEmpty(branchid)?"":branchid;
                //获取tableName
                String from = config.getZxSql().toUpperCase().substring(config.getZxSql().toUpperCase().indexOf("FROM") + 4).trim();
                String tableName = from.substring(0, from.indexOf(" "));
                //拼接sql
                String sql = String.format(config.getZxSql(), param, branchid, branchid);
                //查询比较数据
                queryAndCompareDataBySql(apiId, "", compareData, record.getType(), sql, tableName);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private List<String> getParams(LogRecord record, ConfigApiIDPolicyDTO config) {
        List<String> params = new ArrayList<>();
        if (config.getDigit().contains(",")) {
            //参数需要拼接
            String[] digits = config.getDigit().split(",");
            StringBuffer paramTmp = new StringBuffer();
            for (String digit: digits) {
                paramTmp.append(getQueryParam(record, digit));
            }
            params.add(paramTmp.toString());
        } else if (config.getDigit().contains("|")) {
            //参数需要循环
            String[] digits = config.getDigit().split("\\|");
            String[] firstPositions = digits[0].split("-");
            for (int i = 0; i < Integer.parseInt(digits[2]); i++) {
                Integer start = Integer.parseInt(firstPositions[0]) + (Integer.parseInt(digits[1]) * i);
                Integer end = Integer.parseInt(firstPositions[1]) + (Integer.parseInt(digits[1]) * i);
                params.add(getQueryParam(record, start + "-" + end));
            }
        } else {
            //单个参数
            params.add(getQueryParam(record, config.getDigit()));
        }
        return params;
    }

    /**
     * 单个policy
     * @param record
     * @param positions
     * @return
     */
    private String getQueryParam(LogRecord record, String positions) {
        String[] split = positions.split("-");
        //截取参数
        return record.getRequestBody().substring(Integer.parseInt(split[0]) - 1, Integer.parseInt(split[1]));
    }
}
